namespace Microsoft.Robotics.Vision.Runtime.Cameras
{
    using System;
    using System.Drawing;
    using System.Runtime.Serialization;    
    using Microsoft.Robotics.Core.Algorithms;
    using Microsoft.Robotics.Runtime;
    using Microsoft.Robotics.Tracing;
    using Microsoft.Robotics.Vision;
    using Microsoft.Robotics.Vision.Cameras;

    /// <summary>
    /// Agent that does kinect frame transformation.
    /// </summary>
    [DataContract]
    public class KinectFrameDownScalingAgent : ConsumerProducerAgent<DepthCameraAgentMessage<KinectDepthCameraParameters>, DepthCameraAgentMessage<KinectDepthCameraParameters>>
    {
        /// <summary>
        /// Frequency down scaling factor
        /// </summary>
        [DataMember(Name = "FrequencyDownScaleFactor")]
        private readonly int? frequencyDownScaleFactor;

        /// <summary>
        /// Resolution down scaling factor
        /// </summary>
        [DataMember(Name = "ResolutionDownScaleFactor")]
        private readonly int? resolutionDownScaleFactor;
      
        /// <summary>
        /// Frequency throttle
        /// </summary>
        private FrequencyThrottler throttle;

        /// <summary>
        /// Initializes a new instance of the <see cref="KinectFrameDownScalingAgent"/> class. 
        /// </summary>
        /// <param name="name">Agent name</param>
        /// <param name="frequencyDownScaleFactor">Frequency down scaling factor</param>
        /// <param name="resolutionDownScaleFactor">Resolution down scaling factor</param>
        /// <param name="producer">Message producer - normally KinectAgent</param>
        internal KinectFrameDownScalingAgent(
                        string name,
                        int frequencyDownScaleFactor,
                        int resolutionDownScaleFactor,
                        IProducer<DepthCameraAgentMessage<KinectDepthCameraParameters>> producer)
                        : base(name, producer.Name)
        {
            this.frequencyDownScaleFactor = frequencyDownScaleFactor;
            this.resolutionDownScaleFactor = resolutionDownScaleFactor;
        }

        /// <summary>
        /// Called after construction, to allow the agent to finish initialization. 
        /// </summary>
        /// <param name="locator">A locator that can be used to find other agents.</param>
        public override void Initialize(AgentLocator locator)
        {
            base.Initialize(locator);

            if (!this.frequencyDownScaleFactor.HasValue || !this.resolutionDownScaleFactor.HasValue)
            {
                TraceOut.Error(TraceContexts.Nav, "Invalid values specified for frequency or resolution down-scaling");
                throw new ArgumentException("Invalid values specified for frequency or resolution down-scaling");
            }
            else
            {
                this.throttle = new FrequencyThrottler(this.frequencyDownScaleFactor.Value);
            }
        }

        /// <summary>
        /// Receive, process and republish a message
        /// </summary>
        /// <param name="message">Message to process</param>
        public override void Receive(DepthCameraAgentMessage<KinectDepthCameraParameters> message)
        {
            if (this.throttle.Throttle())
            {
                Tuple<short[], Size> scaledImage = ImageOperations2D.Downscale<short>(
                    message.ImageFrame.ImageData,
                    new System.Drawing.Size(message.ImageFrame.Width, message.ImageFrame.Height),
                    this.resolutionDownScaleFactor.Value);

                ImageFrame<short> newDepthFrame = new ImageFrame<short>(scaledImage.Item2.Width, scaledImage.Item2.Height);

                for (int i = 0; i < newDepthFrame.ImageData.Length; i++)
                {
                    newDepthFrame[i] = message.ImageFrame[i];
                }

                // we use null as the ImageFrameDepth parameter, as we dont want it to serialize across (and as of 10/29/2013 generic array type serialization is very suboptimal)
                    DepthCameraAgentMessage<KinectDepthCameraParameters> processedImage = new DepthCameraAgentMessage<KinectDepthCameraParameters>(
                    message.OriginatingTime,
                        newDepthFrame,
                        message.Parameters);

                    this.Publisher.Post(processedImage);                
            }
        }
    }
}
